﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Epsilon.Expr;

namespace Epsilon.Parse
{
    public partial class Parser
    {
        public List<PidLabel> ParsePidLabels()
        {
            List<PidLabel> labels = new List<PidLabel>();
            labels.Add(ParsePidLabel());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                labels.Add(ParsePidLabel());
            }
            return labels;
        }

        public PidLabel ParsePidLabel()
        {
            if (MatchPeek(TokenType.Symbol, "("))
            {
                Match(TokenType.Symbol, "(");
                PidLabel label = ParsePidLabel();
                Match(TokenType.Symbol, ")");
                return label;
            }
            else if (MatchPeek(TokenType.Symbol, "{"))
            {
                Chomp();
                if (CheckType(TokenType.Atom))
                {
                    UnChomp();
                    return ParsePidBase();
                }
                else if (MatchPeek(TokenType.Symbol, "}"))
                {
                    Chomp();
                    return new PidBase(null, null);
                }
                else
                {
                    List<PidLabel> labels = ParsePidLabels();
                    Match(TokenType.Symbol, "}");
                    return new PidTuple(labels);
                }
            }
            else if (MatchPeek(TokenType.Keyword, "fun"))
            {
                Match(TokenType.Keyword, "fun");
                Match(TokenType.Symbol, "(");
                List<PidRLabel> args = ParsePidRLabels();
                Match(TokenType.Symbol, ")");
                Match(TokenType.Symbol, "->");
                PidLabel ret = ParsePidLabel();
                return new PidFun(args, ret);
            }
            else if (CheckType(TokenType.Variable))
                return ParsePidBase();
            else
                Error("Invalid process id label");
            return null;
        }

        public PidRLabel ParsePidRLabel()
        {
            if (MatchPeek(TokenType.Symbol, "{"))
            {
                Match(TokenType.Symbol, "{");
                List<PidRLabel> labels = ParsePidRLabels();
                Match(TokenType.Symbol, "}");
                return new PidRTuple(labels);
            }
            else if (MatchPeek(TokenType.Symbol, "("))
            {
                Match(TokenType.Symbol, "(");
                PidRLabel label = ParsePidRLabel();
                Match(TokenType.Symbol, ")");
                return label;
            }
            else if (MatchPeek(TokenType.Keyword, "fun"))
            {
                Match(TokenType.Keyword, "fun");
                Match(TokenType.Symbol, "(");
                List<PidRLabel> args = ParsePidRLabels();
                Match(TokenType.Symbol, ")");
                Match(TokenType.Symbol, "->");
                PidRLabel ret = ParsePidRLabel();
                return new PidRFun(args, ret);
            }
            else if (CheckType(TokenType.Variable))
            {
                return ParsePidVar();
            }
            else
            {
                Error("Invalid restricted process id label");
            }
            return null;
        }

        public PidBase ParsePidBase()
        {
            List<PidExpr> pids = null;
            List<PidVar> pidvs = null;
            if (MatchPeek(TokenType.Symbol, "{"))
            {
                Match(TokenType.Symbol, "{");
                pids = ParsePids();
                Match(TokenType.Symbol, "}");
                return new PidBase(pidvs, pids);
            }
            pidvs = ParsePidVars();
            if (MatchPeek(TokenType.Symbol, "+"))
            {
                Match(TokenType.Symbol, "+");
                Match(TokenType.Symbol, "{");
                pids = ParsePids();
                Match(TokenType.Symbol, "}");
            }

            return new PidBase(pidvs, pids);
        }

        public List<PidRLabel> ParsePidRLabels()
        {
            List<PidRLabel> labels = new List<PidRLabel>();
            labels.Add(ParsePidRLabel());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                labels.Add(ParsePidRLabel());
            }
            return labels;
        }

        public List<PidExpr> ParsePids()
        {
            List<PidExpr> pids = new List<PidExpr>();
            pids.Add(ParsePid());
            while (MatchPeek(TokenType.Symbol, ","))
            {
                Chomp();
                pids.Add(ParsePid());
            }
            return pids;
        }

        public List<PidVar> ParsePidVars()
        {
            List<PidVar> vars = new List<PidVar>();
            vars.Add(ParsePidVar());
            while (MatchPeek(TokenType.Symbol, "+"))
            {
                Chomp();
                if (CheckType(TokenType.Variable))
                    vars.Add(ParsePidVar());
                else
                {
                    UnChomp();
                    break;
                }
            }
            return vars;
        }

        public PidExpr ParsePid()
        {
            if (CheckType(TokenType.Atom))
            {
                Token t = CurrentToken();
                if (t.Value.StartsWith("_"))
                    Error("User-defined process id's cannot start with '_'");
                PidExpr p = new PidExpr(t.Value, t.Line, t.Col);
                Chomp();
                return p;
            }
            else
            {
                Error("Expected a process id atom");
            }
            return null;
        }

        public PidVar ParsePidVar()
        {
            if (CheckType(TokenType.Variable))
            {
                PidVar v = new PidVar(CurrentToken().Value);
                Chomp();
                return v;
            }
            else
            {
                Error("Expected a variable");
            }
            return null;
        }
    }
}
